package com.gcxy.security.filters;

import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.gcxy.config.warpper.BufferedRequestWrapper;
import com.gcxy.config.warpper.RequestBodyWrapper;
import com.gcxy.core.R;
import com.gcxy.enums.ResultCodeEnum;
import com.gcxy.utils.JwtTokenUtil;
import com.gcxy.utils.RedisTemplateUtil;
import com.gcxy.utils.SecurityUtil;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.annotation.Resource;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;

/**
 * Jwt登录授权过滤器
 *
 * @author pep7chiao
 * @date 2024/7/7
 */
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
    /**
     * Jwt存储的请求头
     */
    @Value("${jwt.tokenHeader}")
    private String tokenHeader;
    @Resource
    private JwtTokenUtil jwtTokenUtil;
    @Resource
    private UserDetailsService userDetailsService;
    @Resource
    private RedisTemplateUtil redisTemplateUtil;
    @Value("${login.white.list}")
    private List<String> whiteList;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
//        request = new BufferedRequestWrapper(request);
        // 获取请求内容类型
        String contentType = request.getContentType();
        if (ObjectUtils.isEmpty(contentType) || !contentType.startsWith("multipart/form-data")) {
            request = new RequestBodyWrapper(request);
        }
        // 1. 从当前请求中获取到授权数据
        String authHeader = request.getHeader(tokenHeader);
        // 2. 判断获取到的请求头中是否存在token
        if (authHeader != null ) {
            // 从token中获取到当前登录用户的用户名
            String username = jwtTokenUtil.getUserNameFromToken(authHeader);
            //查询redis是否有对应的token，如果没有，说明已经过期或者已经退出
            String redisUserName = redisTemplateUtil.getString(authHeader);
            //获取当前请求路径
            String requestURI = request.getRequestURI();
            //判断当前请求路径是否在白名单中
            boolean allowedListContains = SecurityUtil.isAllowedListContains(requestURI, whiteList);
            if ((redisUserName == null || !username.equals(redisUserName)) && !allowedListContains) {
                // 设置编码格式
                response.setCharacterEncoding("UTF-8");
                response.setContentType("application/json");
                PrintWriter out = response.getWriter();
                R bean = R.Failed(ResultCodeEnum.TOKEN_EXPIRED_REDIRECT.getCode(), ResultCodeEnum.TOKEN_EXPIRED_REDIRECT.getMessage());
                out.write(new ObjectMapper().writeValueAsString(bean));
                out.flush();
                out.close();
                return;
            }

            // 如果token存在用户名，但是获取不到用户对象说明用户未登录
            if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
                // 执行登录操作
                UserDetails userDetails = userDetailsService.loadUserByUsername(username);
                // 同时判断token是否有效，如果有效则重新设置用户对象
                if (jwtTokenUtil.validateToken(authHeader, userDetails)) {
                    UsernamePasswordAuthenticationToken authenticationToken =
                            new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
                    authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                    SecurityContextHolder.getContext().setAuthentication(authenticationToken);
                }
            }
        }
        // 放行
        filterChain.doFilter(request, response);
    }
}
